FFMPEG录屏(7) 您所在的位置:网站首页 ffmpeg 录屏 鼠标 FFMPEG录屏(7)

FFMPEG录屏(7)

2024-07-15 00:12| 来源: 网络整理| 查看: 265

补充通过GDI捕获桌面方法

微软API和DEMO

GDI Capturing an Image Desktop Duplication API

建议是WIN7使用GDI(录制时关闭AERO会有效率上的显著提升),WIN8及以上使用Duplication方式进行捕获。

捕获流程(翻译过来会走味。。。还是自行体会)

To store an image temporarily, your application must call CreateCompatibleDC to create a DC that is compatible with the current window DC. After you create a compatible DC, you create a bitmap with the appropriate dimensions by calling the CreateCompatibleBitmap function and then select it into this device context by calling the SelectObject function.

After the compatible device context is created and the appropriate bitmap has been selected into it, you can capture the image. The BitBlt function captures images. This function performs a bit block transfer that is, it copies data from a source bitmap into a destination bitmap. However, the two arguments to this function are not bitmap handles. Instead, BitBlt receives handles that identify two device contexts and copies the bitmap data from a bitmap selected into the source DC into a bitmap selected into the target DC. In this case, the target DC is the compatible DC, so when BitBlt completes the transfer, the image has been stored in memory. To redisplay the image, call BitBlt a second time, specifying the compatible DC as the source DC and a window (or printer) DC as the target DC.

大概意思就是说先获取桌面的DC句柄,然后创建一个兼容句柄,再利用BitBlt函数拷贝图像数据到创建的兼容句柄中,可以在此时进行截取、缩放、绘制鼠标、绘制LOGO等,最后通过GetDIBits函数获取RGB数据,保存或者压缩即可。

获取桌面DC HDC hdc_screen = GetWindowDC(NULL); 创建兼容DC HDC hdc_mem = CreateCompatibleDC(hdc_screen); 根据桌面DC创建兼容位图句柄HBITMAP int _width = GetSystemMetrics(SM_CXVIRTUALSCREEN); int _height = GetSystemMetrics(SM_CYVIRTUALSCREEN); HBITMAP hbm_mem = CreateCompatibleBitmap(hdc_screen, _width, _height);

值得一提的是,此处width,height取值为桌面大小,在实际使用中可以是目标区域大小、目标窗口大小。

将位图选入创建好的兼容句柄中 SelectObject(hdc_mem, hbm_mem);

请注意此函数的返回值,当进行绘图时,如果hdc_mem是一个重复使用或者是一个目标窗口的DC时,你可能需要主动释放返回的Object,否则会造成内存泄漏。

拷贝图像到兼容句柄中 //must have CAPTUREBLT falg,otherwise some layered window can not be captured BitBlt(hdc_mem, 0, 0, _width, _height, hdc_screen, _rect.left, _rect.top, SRCCOPY | CAPTUREBLT)

这里又有大坑了,请Google搜索CAPTUREBLT标志,因为它将导致你的鼠标不停地闪烁。据说这是微软的机制,会在每次调用此函数的时候对鼠标进行Show和Hide操作,说是要得到纯净的图像????WTF!! 在Windows中,当窗体含有WS_EX_LAYERED标志时,如果没有CAPTUREBLT标志将无法捕获到。

CAPTUREBLT Includes any windows that are layered on top of your window in the resulting image. By default, the image only contains your window. Note that this generally cannot be used for printing device contexts.

所以吗请酌情附加此标志,我所遇到的场景是在共享桌面的同时进行着视频通话,其中双方的视频小窗口就是个Layered标志的窗口,因此需要附加CAPTUREBLT。

绘制鼠标 void record_desktop_win_gdi::draw_cursor(HDC hdc) { if (!(_ci.flags & CURSOR_SHOWING)) return; //is cursor in the tartet zone if (_ci.ptScreenPos.x _rect.right || _ci.ptScreenPos.y _rect.bottom ) return; HICON icon; ICONINFO ii; icon = CopyIcon(_ci.hCursor); if (!icon) return; int dstx = 0, dsty = 0; dstx = abs(_ci.ptScreenPos.x - _rect.left); dsty = abs(_ci.ptScreenPos.y - _rect.top); if (GetIconInfo(icon, &ii)) { POINT pos; DrawIconEx(hdc, dstx, dsty, icon, 0, 0, 0, NULL, DI_NORMAL); DeleteObject(ii.hbmColor); DeleteObject(ii.hbmMask); } DestroyIcon(icon); } CURSORINFO _ci; if (GetCursorInfo(&_ci)) { draw_cursor(hdc_mem); } 初始化位图信息 BITMAPINFOHEADER bi; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = _width; bi.biHeight = _height * (-1); bi.biPlanes = 1; bi.biBitCount = 32;//should get from system bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0;

一个有趣的事是你需要将bi.biHeight设置为负值,否则保存的图像将是反转的。

获取位图RGB数据 //scan colors by line order GetDIBits(hdc_mem, hbm_mem, 0, _height, _buffer, (BITMAPINFO*)&bi, DIB_RGB_COLORS); 释放句柄 if(hbm_mem) DeleteObject(hbm_mem); if(hdc_mem) DeleteObject(hdc_mem); if(hdc_screen) ReleaseDC(NULL, hdc_screen);

至此已经完成了GDI方式的桌面捕获,_buffer为RGB数据缓存。

保存BMP文件预览 //save bmp to test BITMAPFILEHEADER bf; bf.bfType = 0x4d42; bf.bfReserved1 = 0; bf.bfReserved2 = 0; bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bf.bfSize = bf.bfOffBits + _width * _height * 4; FILE *fp = fopen("..\\..\\save.bmp", "wb+"); fwrite(&bf, 1, sizeof(bf), fp); fwrite(&bi, 1, sizeof(bi), fp); fwrite(_buffer, 1, _buffer_size, fp); fflush(fp); fclose(fp);

至于buffer大小,在这里我们的RGB数据存储格式为BGRA,什么意思呢就是整个屏幕安装宽高进行线性扫描(感谢第一份工作老板的解释),从屏幕左上角开始,每一个像素点均有Red、Green、Blue、Alpha表示,也就是说每一个像素点占用4byte。从左至右从高到低进行存储,所以BGRA数据大小为width*height*4。 这也是GetDIBits函数中第三个(start)第四个参数(cline)的填法解释。

GitHub传送门

screen-recorder



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有